package config

import (
	"abdis/lib/logger"
	"bufio"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// Properties holds global config properties
var (
	Properties         *ServerProperties
	EachTimeServerInfo *ServerInfo
)

type ServerProperties struct {
	Bind        string `cfg:"bind"`
	Port        int    `cfg:"port"`
	Databases   int    `cfg:"databases"`
	Version     string `cfg:"version"`
	RequirePass string `cfg:"requirepass"`
	AppendOnly  bool   `cfg:"appendonly"`
}

type ServerInfo struct {
	StartUpTime time.Time
}

func init() {
	EachTimeServerInfo = &ServerInfo{
		StartUpTime: time.Now(),
	}

	Properties = &ServerProperties{
		Bind:        "0.0.0.0",
		Port:        6399,
		Databases:   16,
		Version:     "0.0.10",
		RequirePass: "",
		AppendOnly:  false,
	}
}

func parse(file io.Reader, config *ServerProperties) {
	rawMap := make(map[string]string)
	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		line := scanner.Text()
		line = strings.TrimLeft(line, " ")
		line = strings.TrimRight(line, " ")
		if len(line) > 0 && line[0] == '#' || line == "" {
			continue
		}

		pivot := strings.IndexAny(line, " ")
		if pivot <= -1 {
			continue
		}
		key := line[0:pivot]
		val := line[pivot+1:]
		rawMap[strings.ToLower(key)] = val
	}

	if err := scanner.Err(); err != nil {
		logger.Fatal(err)
	}

	// start parse
	t := reflect.TypeOf(config)
	v := reflect.ValueOf(config)
	n := t.Elem().NumField()
	for i := 0; i < n; i++ {
		field := t.Elem().Field(i)
		val := v.Elem().Field(i)
		key, ok := field.Tag.Lookup("cfg")
		if !ok {
			key = field.Name
		}
		value, ok := rawMap[strings.ToLower(key)]
		if !ok {
			continue
		}

		switch field.Type.Kind() {
		case reflect.String:
			val.SetString(value)
		case reflect.Int:
			if num, err := strconv.ParseInt(value, 10, 64); err == nil {
				val.SetInt(num)
			}
		case reflect.Bool:
			val.SetBool(value == "yes")
		case reflect.Slice:
			if field.Type.Elem().Kind() == reflect.String {
				slice := strings.Split(value, ",")
				val.Set(reflect.ValueOf(slice))
			}
		}
	}
}

func SetUpConfig(path string) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	parse(file, Properties)
}
